<?php
require_once 'Zend/Controller/Front.php';

require_once 'Zend/Loader.php';

require_once 'Zend/Session/Namespace.php';

class Impact_Pagination 
{
    protected static $_namePrefix = '';
    protected static $_pgClass = 'Impact_Pagination';

    protected $_name;
    protected $_pageNum = 0;
    protected $_perPage = 10;
    protected $_numPages = 0;
    protected $_cnt = 0;
    protected $_sortCol = '';
    protected $_sortColIdx = -1;
    protected $_sortDir = 'down';
    protected $_updatePermissions;

    public function __construct($name = null)
    {
        if (null === $name) {
            $name = self::_getDefaultName();
        }

        $this->_name = $name;
        $this->_updatePermissions = $this->_getDefaultUpdatePermissions();
    }

    public static function setPaginationClassName($name)
    {
        self::$_pgClass = $name;
    }

    public static function getPaginationClassName()
    {
        return self::$_pgClass;
    }

    public static function setNamePrefix($namePrefix)
    {
        self::$_namePrefix = $namePrefix;
    }

    public static function getNamePrefix()
    {
        return self::$_namePrefix;
    }

    public function setUpdatePermission($name, $value)
    {
        $this->_updatePermissions[$name] = (boolean) $value;
        return $this;
    }

    public function getUpdatePermission($name)
    {
        return $this->_updatePermissions[$name];
    }

    public function setPageNumber($pageNumber)
    {
        $this->_pageNum = max(0, min($pageNumber, $this->_numPages));
    }

    public function setCount($count)
    {
        $this->_adjustCount($count);

        return $this;
    }

    public function setItemsPerPage($perPage)
    {
        $this->_perPage = $perPage;

        return $this;
    }

    public function update($count, array $cols = array(), $sortDefault = 0, $downDefault = true)
    {   
        if ($this->_sortColIdx == -1 and count($cols) > 0) {
            $this->_sortColIdx = $sortDefault;

            if (is_array($cols[$sortDefault])) {
                $this->_sortCol = $cols[$sortDefault][0];
                $this->_sortDir = $cols[$sortDefault][1];
            } else {
                $this->_sortCol = $cols[$sortDefault];
                $this->_sortDir = ($downDefault ? 'down' : 'up');
            }
        }

        if ($this->_getUpdateParam('pagination') != $this->_name) {
            $this->_adjustCount($count);
            return;
        }

        if ($this->getUpdatePermission('clear') and $this->_updateParamExists('clear')) {
            $this->_sortColIdx = $sortDefault;
            $this->_pageNum = 0;

            if (is_array($cols[$sortDefault])) {
                $this->_sortCol = $cols[$sortDefault][0];
                $this->_sortDir = $cols[$sortDefault][1];
            } else {
                $this->_sortCol = $cols[$sortDefault];
                $this->_sortDir = ($downDefault ? 'down' : 'up');
            }
        }

        if ($this->getUpdatePermission('pageNum') and $this->_updateParamExists('pageNum')) {
            $n = intval($this->_getUpdateParam('pageNum'));
            $this->_pageNum = $n;
        }

        if ($this->getUpdatePermission('itemsPerPage') and $this->_updateParamExists('itemsPerPage')) {
            $n = min(100, max(0, intval($this->_getUpdateParam('itemsPerPage'))));
            $this->_perPage = $n;
        }

        if ($this->getUpdatePermission('sort') and $this->_updateParamExists('sort')) {
            $sortIdx = intval($this->_getUpdateParam('sort'));

            if ($sortIdx >= 0 and $sortIdx < count($cols)) {
                if (is_array($cols[$sortIdx]))
                {               
                    $this->_sortCol = $cols[$sortIdx][0];
                    $this->_sortDir = $cols[$sortIdx][1];
                } else {
                    $this->_sortCol = $cols[$sortIdx];
                    $this->_sortDir = ($downDefault ? 'down' : 'up');
                }
                $this->_sortColIdx = $sortIdx;
            } else {
                if (is_array($cols[$sortIdx])) {
                    $this->_sortCol = $cols[$sortDefault][0];
                    $this->_sortDir = $cols[$sortDefault][1];
                }
                else {
                    $this->_sortCol = $cols[$sortDefault];
                    $this->_sortDir = ($downDefault ? 'down' : 'up');
                }
                
                $this->_sortColIdx = $sortDefault;
            }
        }

        if ($this->getUpdatePermission('sortDir') and $this->_updateParamExists('sortDir')) {
            if ($this->_getUpdateParam('sortDir') == 'up') {
                $this->_sortDir = 'up';
            } else {
                $this->_sortDir = 'down';
            }
        }

        $this->_adjustCount($count);

        return $this;
    }

    public function __get($name)
    {
        switch ($name) {
            case 'name':
                return $this->_name;
            case 'prevPage':
                return $this->_pageNum-1;
            case 'nextPage':
                return $this->_pageNum+1;
            case 'firstPage':
                return 0;
            case 'lastPage':
                return max(0, $this->_numPages-1);
            case 'isFirstPage':
                return ($this->_pageNum == 0);
            case 'isLastPage':
                return ($this->_pageNum >= $this->_numPages-1);
            case 'offset':
                return $this->_perPage*$this->_pageNum;
            case 'itemsPerPage':
                return $this->_perPage;
            case 'numPages':
                return max(1,$this->_numPages);
            case 'pageNum':
                return $this->_pageNum+1;
            case 'numRows':
                return $this->_cnt;
            case 'sortColumn':
                return $this->_sortCol;
            case 'sortColumnIndex':
                return $this->_sortColIdx;
            case 'sortDirection':
                return $this->_sortDir;
            case 'sortDirectionSql':
                return ($this->_sortDir == 'down' ? 'ASC' : 'DESC');
            case 'orderBy':
                return $this->_sortCol
                    . ' '
                    . ($this->_sortDir == 'down' ? 'ASC' : 'DESC');
            case 'firstRowNum':
                if ($this->_cnt == 0) {
                    return 0;
                } else {
                    return $this->offset+1;
                }
            case 'lastRowNum':
                return min($this->_cnt, $this->offset+$this->_perPage);
        }
    }

    public function getSortControlDirection($idx)
    {
        if ($idx == $this->_sortColIdx) {
            return ($this->_sortDir == 'up' ? 'down' : 'up');
        }
        else {
            return 'down';
        }
    }

    public function getPageList($count = 10, $startAtZero = false)
    {
        $a = $count / 2;
        $l = $this->_pageNum - $a;
        $h = $this->_pageNum + $a;

        $l -= max(0, $h - ($this->_numPages-1));
        $h += -min(0, $this->_pageNum - $a);

        $l = max(0, $l);
        $h = min($h, $this->_numPages - 1);

        $start = $l + ($startAtZero ? 0 : 1);
        $end   = $h + ($startAtZero ? 0 : 1);

        if ($start > $end) {
            return array($start);
        } else {
            return range($start, $end);
        }
    }

    public static function get($name = null)
    {
        if (null === $name) {
            $name = self::_getDefaultName();
        } else {
            $name = self::$_namePrefix . $name;
        }

        $namespace = new Zend_Session_Namespace($name);

        if ($namespace->pagination instanceof Impact_Pagination) {
            return $namespace->pagination;
        } else {
            Zend_Loader::loadClass(self::$_pgClass);
            $class = new ReflectionClass(self::$_pgClass);
            $namespace->pagination = $class->newInstance($name);
            return $namespace->pagination;
        }
    }

    protected static function _getDefaultName()
    {
        $request = Zend_Controller_Front::getInstance()->getRequest();

        return self::$_namePrefix
            . $request->getControllerName() 
            . '-' 
            . $request->getActionName();
    }

    protected function _getDefaultUpdatePermissions()
    {
        return array(
            'clear'        => true,
            'pageNum'      => true,
            'itemsPerPage' => true,
            'sort'         => true,
            'sortDir'      => true);
    }

    protected function _adjustCount($count)
    {
        if ($count !== null) {
            $this->_cnt = $count;
            $this->_numPages = (int) ceil($this->_cnt / $this->_perPage);
        }

        if ($this->_pageNum > $this->_numPages-1) {
            $this->_pageNum = max(0, $this->_numPages-1);
        }
    }

    protected function _updateParamExists($name)
    {
        $request = Zend_Controller_Front::getInstance()->getRequest();
        return null !== $request->getParam($name);
    }

    protected function _getUpdateParam($name)
    {
        $request = Zend_Controller_Front::getInstance()->getRequest();
        return $request->getParam($name, '');
    }
}

// vim: sw=4:ts=4:sts=4:et
